#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "Task.hpp" // 发任务的文件，.hpp -> .h和.cpp写一起

#define PROCESS_NUM 5 // 创建的子进程数目

using namespace std;

int waitCommand(int waitFd, bool& quit) //如果对方不发，我们就阻塞
{
    uint32_t command = 0; // uint32_t四个字节
    ssize_t s = read(waitFd, &command, sizeof(command)); // 期望读取四个字节
    if (s == 0) // 读到0让子进程退出
    {
        quit = true;
        return -1;
    }
    assert(s == sizeof(uint32_t)); // 不是四个字节就报错
    return command;
}

void sendAndWakeup(pid_t who, int fd, uint32_t command)   // 通过文件描述符，向哪一个文件发什么命令
{               // who给哪个进程，这个进程的id
    write(fd, &command, sizeof(command));
    cout << "main process: call process " << who << " execute " << desc[command] << " through " << fd << endl;
}

int main()
{
    // 代码中关于fd的处理，有一个小问题，不影响我们使用，但是你能找到吗？？
    load();
    vector<pair<pid_t, int>> slots; // 存放子进程pid和子进程写端id(pipefd)
    vector<int> deleteFd; // 存放要删除的子进程写端fd(不删除也不会出问题)
    for (int i = 0; i < PROCESS_NUM; i++) // 先创建多个进程
    {
        int pipefd[2] = { 0 };
        int ret = pipe(pipefd); // 创建管道
        assert(ret == 0); // 等于0才创建成功
        (void)ret;

        pid_t id = fork();
        assert(id != -1);
        if (id == 0) // 子进程，进行读取
        {
            close(pipefd[1]); // 关闭写端
            for (int i = 0; i < deleteFd.size(); i++) // 关闭所以继承下来的写端fd
            {
                close(deleteFd[i]);
            }
            while (true)
            {
                // 等命令
                bool quit = false; // 默认不退出
                int command = waitCommand(pipefd[0], quit); // 如果对方不发，我们就阻塞
                if (quit) // 读到0就退出关闭所有进程
                {
                    break;
                }
                if (command >= 0 && command < handlerSize()) // 执行对应的命令
                {                                            // handlerSize任务方法的个数
                    callbacks[command]();
                }
                else
                {
                    cout << "非法command: " << command << endl;
                }
            }
            exit(1);
        }

        close(pipefd[0]); // 父进程，进行写入，关闭读端
        slots.push_back(pair<pid_t, int>(id, pipefd[1])); // 把此次循环得到的子进程id和子进程写端的id保存
        deleteFd.push_back(pipefd[1]); // 把要被继承下去的子进程写端fd保存起来
    }

    // 父进程均衡地派发任务（单机版的负载均衡）
    srand((unsigned long)time(nullptr) ^ getpid() ^ 2335643123L); // 仅仅让数据源更随机
    while (true)
    {
        // 选择一个任务
        int command = rand() % handlerSize();
        // 选择一个进程 ，采用随机数的方式，选择进程来完成任务，随机数方式的负载均衡
        int choice = rand() % slots.size();
        // 把任务给指定的进程
        sendAndWakeup(slots[choice].first, slots[choice].second, command);
        sleep(1);

         //int select; // 手动版
         //int command;
         //cout << "############################################" << endl;
         //cout << "#   1. show funcitons      2.send command  #" << endl;
         //cout << "############################################" << endl;
         //cout << "Please Select> ";
         //cin >> select;
         //if (select == 1)
         //{
         //    showHandler();
         //}
         //else if (select == 2)
         //{
         //    cout << "Enter Your Command: ";
         //    cin >> command; // 选择任务
         //    int choice = rand() % slots.size(); // 选择进程
         //    sendAndWakeup(slots[choice].first, slots[choice].second, command); // 把任务给指定的进程
         //}
         //else // 选择错误
         //{
         //}
    }

    for (const auto& slot : slots) // 关闭fd, 所有的子进程都会退出
    {
        close(slot.second);
    }

    for (const auto& slot : slots) // 回收所有的子进程信息
    {
        waitpid(slot.first, nullptr, 0);
    }
}